package net.ubinow.mflex.baseui;

import javax.microedition.lcdui.Graphics;

import net.ubinow.mflex.data.IDataBinder;
import net.ubinow.mflex.data.IDataItem;
import net.ubinow.mflex.events.AbsEventPublisher;
import net.ubinow.mflex.events.Event;
import net.ubinow.mflex.util.Constants;

/**
 *
 * @author Josh
 */
public abstract class AbsWidget extends AbsEventPublisher implements IWidget, IDataBinder {
    
    protected String _id = Constants.UNDEFINED;
    
    /** should be set for each new widget type **/ 
    protected int _type = Constants.WIDGET_TYPE_UNDEFINED; 
    protected int _x = 0;
    protected int _y = 0;
    protected int _width = 0;
    protected int _height = 0;
    protected int _paddingW = 0;
    protected int _paddingH = 0; 
    
    protected boolean _focus = false;     // widget currently selected
    protected boolean _selectable = true;    // possible to selected widget (labels = no, textbox = yes, ...)
    
    protected IDataItem[] _items = null;
    protected String[] _displayText = null; // associated with the data items; this is the text used to display to the user
    protected int _itemsCount = 0; 
    protected int _selectedIndex = 0;       
    
    protected boolean _setBased = false; // if set to true then duplicate objects cannot be added to the items array
    
    protected WFont _font = null; 
    
    protected boolean _isVisible = true;
    
    private int _prevClipX = 0;
    private int _prevClipY = 0;
    private int _prevClipW = 0;
    private int _prevClipH = 0;        
    
    /** Creates a new instance of AbsWidget */
    public AbsWidget() {
    }
    
    /**
     * returns a identifier of the type of widget this instance is; this is to replace the usage of instanceof 
     * (a little quicker)
     **/
    public int getWidgetType(){
        return _type;
    }
    
    /** each widget should implement this to set the classes widgets type **/ 
    protected abstract void setWidgetType();
    
    //<editor-fold defaultstate="collapsed" desc="IWidget implementation">
    
    public String getID(){
        return _id; 
    }
    
    public void setID( String id ){
        _id = id; 
    }
    
    public int getPaddingW(){
        return _paddingW; 
    }
    
    public void setPaddingW( int padding ){
        _paddingW = padding; 
    }
    
    public int getPaddingH(){
        return _paddingH; 
    }
    
    public void setPaddingH( int padding ){
        _paddingH = padding;
    }
    
    public void setFont( WFont font ){
        _font = font; 
    }
    
    public WFont getFont(){
        return _font; 
    }
    
    public void setPos( int x, int y ){
        _x = x;
        _y = y; 
    }
    
    public int getX() {
        return _x;
    }
    
    public void setX(int x) {
        _x = x;
    }
    
    private int _orgX = -1;
    private int _orgY = -1;
    
    public void setOrgPos( int x, int y ){
        _orgX = x; 
        _orgY = y; 
        resetPos();
    }
    
    public void resetPos(){
        _x = _orgX;
        _y = _orgY;
    } 
    
    public int getOrgX(){
        return _orgX;
    }
    public int getOrgY(){
        return _orgY;
    }
    
    public int getBottomY(){
        return getBottomY( false );
    }
    
    public int getBottomY( boolean includeHeight ){
        if( includeHeight )
            return getY() + getHeight();
        else
            return getY();
    }
    
    public int getY() {
        return _y;
    }
    
    public void setY(int y) {
        _y = y;
    }
    
    public int getWidth() {
        return _width;
    }
    
    public int getTotalWidth(){
        return _width + _paddingW;
    }
    
    public void setWidth(int w) {
        _width = w;
    }
    
    public int getHeight() {
        return _height;
    }
    
    public int getTotalHeight(){
        return _height + _paddingH;
    }
    
    public void setHeight(int h) {
        _height = h;
    }        
    
    /** only used by widgets that need more than one layer to render **/ 
    public int maxLayers(){
        return 1;         
    }
    
    public boolean isAnimated(){
        return false; 
    }
    
    public boolean supportsTransitioning(){
        return true; 
    }
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="IDataBinder implementation">
    
    public void setItems( IDataItem[] items ){
        if( items == null )
            return;
        
        _selectedIndex = 0;
        _items = items; 
        _itemsCount = items.length; 
        
        _displayText = new String[_itemsCount];
        for( int i=_itemsCount-1; i>=0; i-- ){
            _displayText[i] =  getDisplayText( _items[i] );
        }
    }
    
    public void addItem( IDataItem item ){
        boolean addItem = true;                 
        
        if( _items == null ){
            _itemsCount = 0; 
            _items = new IDataItem[Constants.AD_ARRAY_EXPAND_FACTOR]; 
            _items[_itemsCount] = item;             
            
            _displayText = new String[Constants.AD_ARRAY_EXPAND_FACTOR];
            _displayText[_itemsCount] = getDisplayText( item );
            
            _itemsCount++; 
            
        }
        else{
            // check to see if it is a set based array, if so then test to ensure that the item doesn't 
            // already exist otherwise skip this test
            int len = _items.length;
            if( _setBased ){                 
                for( int i=len-1; i>=0; i-- ){
                    if( _items[i] != null && _items[i].equals( item ) ){
                        addItem = false;
                        break; 
                    }
                }
            }
            
            if( addItem ){
                // find a place holder else expand the array 
                if( _itemsCount == len ){
                    // expand array
                    IDataItem[] tmp = new IDataItem[len+Constants.AD_ARRAY_EXPAND_FACTOR]; 
                    System.arraycopy( _items, 0, tmp, 0, len ); 
                    _items = tmp; 
                    
                    // expand display text 
                    String[] tmp2 = new String[len+Constants.AD_ARRAY_EXPAND_FACTOR];
                    System.arraycopy( _displayText, 0, tmp2, 0, len );
                    _displayText = tmp2; 
                }
                
                _items[_itemsCount] = item; 
                _displayText[_itemsCount] = getDisplayText( item );
                _itemsCount++; 
                
            }
        }                        
    }
    
    public void insertItem( int index, IDataItem item ){
        boolean addItem = true;                 
        
        if( _items == null ){
            _itemsCount = 0;
            _items = new IDataItem[Constants.AD_ARRAY_EXPAND_FACTOR]; 
            _items[_itemsCount] = item; 
            
            _displayText = new String[Constants.AD_ARRAY_EXPAND_FACTOR];
            _displayText[_itemsCount] = getDisplayText( item );
            
            _itemsCount++; 
        }
        else{
            // check to see if it is a set based array, if so then test to ensure that the item doesn't 
            // already exist otherwise skip this test
            int len = _items.length;
            if( _setBased ){                 
                for( int i=len-1; i>=0; i-- ){
                    if( _items[i] != null && _items[i].equals( item ) ){
                        addItem = false;
                        break; 
                    }
                }
            }
            
            if( addItem ){
                // find a place holder else expand the array 
                if( _itemsCount == len ){
                    // expand array
                    IDataItem[] tmp = new IDataItem[len+Constants.AD_ARRAY_EXPAND_FACTOR]; 
                    System.arraycopy( _items, 0, tmp, 0, len ); 
                    _items = tmp; 
                    
                    // expand display text 
                    String[] tmp2 = new String[len+Constants.AD_ARRAY_EXPAND_FACTOR];
                    System.arraycopy( _displayText, 0, tmp2, 0, len );
                    _displayText = tmp2; 
                }
                
                // move all items along 
                for( int i=_itemsCount; i>index; i-- ){
                    _items[i] = _items[i-1]; 
                    _displayText[i] = _displayText[i-1]; 
                }
                
                // now insert item 
                _items[index] = item; 
                _displayText[index] = getDisplayText( item );
                _itemsCount++; 
                
            }
        }
    }
    
    public void removeItem( IDataItem item ){
        if( _items == null )
            return; 
        
        int len = _items.length; 
        int index = -1; 
        
        for( int i=len-1; i>=0; i-- ){
            if( _items[i].equals( item ) ){
                index = i;
                break; 
            }
        }
        
        // item not found 
        if( index == -1 )
            return; 
        
        for( int i=index; i<_itemsCount; i++ ){
            if( (i+1) < _itemsCount ){
                _items[i] = _items[i+1]; 
                _displayText[i] = _displayText[i+1];
            }            
            else{
                _items[i] = null; 
                _displayText[i] = null; 
            }
        }
        
        _itemsCount--; 
    }
    
    public IDataItem getSelectedItem(){
        if( _items == null )
            return null; 
        
        return _items[_selectedIndex]; 
    }
    
    public String getSelectedText(){
        if( _displayText == null )
            return Constants.UNDEFINED;
        
        if( _displayText.length == 1 )
            return _displayText[0];
        else
            return _displayText[_selectedIndex];
    }
    
    public void setIndex( int index ){
        if( _items == null )
            return; 
        
        // bounds checking 
        if( index == _selectedIndex )
            return;        
        
        // wrap around
        if( index > _items.length )
            index = 0; 
        
        if( index < 0 )
            index = _items.length-1; 
        
        _selectedIndex = index; 
        
        // notify listeners 
        notifySubscribers( getEvet( Event.EVT_INDEX_CHANGED, getSelectedItem() ) ); 
    }
    
    private String _expression = null; 
    
    public void setBindingText(String expression) {
        _expression = expression; 
        
        if( _items != null ){
            initDisplayText(); 
        }
    }
    
    public void removeBindText(){
        _expression = null; 
        
        if( _items != null ){
            initDisplayText(); 
        }
    }      
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="widget public methods">
    
    public void setFocus( boolean focus ,int direction ){
        _focus = focus;
        
        if( focus )
        	notifySubscribers( getEvet( Event.EVT_ON_FOCUS ) );
        else
        	notifySubscribers( getEvet( Event.EVT_OFF_FOCUS ) );
    }
    
    public boolean isFocused(){
        return _focus;
    }
    
    public void setSelectable( boolean selectable ){
        _selectable = selectable;
    }
    
    public boolean isSelectable(){
        return _selectable;
    }        
        
    public void setIsVisible( boolean isVisible ){
        _isVisible = isVisible; 
    }
    
    public boolean isVisible(){
        return _isVisible;
    }    
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="protected methods">
    
    protected void prepareClip( Graphics g, int x, int y, int width, int height ){
        _prevClipX = g.getClipX();
        _prevClipY = g.getClipY();
        _prevClipW = g.getClipWidth();
        _prevClipH = g.getClipHeight();
        
        g.setClip( x, y ,width, height );
    }
    
    protected void releaseClip( Graphics g ){
        g.setClip( _prevClipX, _prevClipY, _prevClipW, _prevClipH );
    }
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="private methods">               
    
    /**
     * instantiates the _displayText; will iterate through all of the data items (_items) and 
     * either fetch the getText() property as the display text or parse the expression text and 
     * and assign it to it's corrosponding _displayText depending if the expression is null or not
     **/
    private void initDisplayText(){
        if( _items == null )
            return; 
        
        if( _displayText == null || _displayText.length != _items.length ){
            _displayText = null;
            _displayText = new String[_items.length]; 
        }
        
        int len = _items.length;         
        for( int i=len-1; i>=0; i-- ){
            _displayText[i] = getDisplayText( _items[i] );
        }
        
    }
    
    /**
     * @return: the parsed expression using the dataitem else the data items getText() property depending if the expression is null or not
     **/
    private String getDisplayText( IDataItem item ){
        // TODO: implement the actual parsing/expression parser - helper class - singleton 
        
        if( _expression == null )
                return item.getText(); 
            else
                return item.getText();                 
    }
    
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="drawing methods">
    
    protected void paintBorder( Graphics g ){
        paintBorder( g, _x, _y, _width, _height, Constants.COLOUR_BLACK );
    }
    
    /**
     * Repaint the border, if any, for this instance.
     *
     * @param g The Graphics object to use for painting operations.
     */
    protected void paintBorder( Graphics g, int x, int y, int width, int height, int colour ) {

       	g.setColor( colour );        	       
        g.drawRect( x, y, width - 1, height - 1);

        paintShadow( g, x, y, width, height );
    }

    /**
     * Repaint the shadow, if any, for this instance.
     *
     * @param g The Graphics object to use for painting operations.
     */
    protected void paintShadow( Graphics g, int x, int y, int width, int height ) {
        int hh;

        hh = getHeight();

        g.setColor( Constants.COLOUR_SHADOW );
        g.drawLine(x + 1, y + hh, x + width, y + hh);
        g.drawLine(x + 2, y + hh + 1, x + width + 1, y + hh + 1);
        g.drawLine(x + width, y + 1, x + width, y + hh);
        g.drawLine(x + width + 1, y + 2, x + width + 1, y + hh + 1);
    }

    /**
     * Paint the bottom and right edge borders, if any, for this instance.
     *
     * @param g The Graphics object to use for painting operations.
     */
    protected void paintLowBorder(Graphics g) {
        paintLowBorder(g, _x, _y, _width, _height, Constants.COLOUR_BLACK );
    }

    /**
     * Paint the bottom and right edge borders, if any, for this instance.
     *
     * @param g The Graphics object to use for painting operations.
     * @param xx The X coordinate of the lower left corner reference point.
     * @param yy The Y coordinate of the lower left corner reference point.
     * @param ww The width of the bottom edge.
     * @param hh The height of the right edge.
     */
    protected void paintLowBorder(Graphics g, int xx, int yy, int ww, int hh, int colour ) {

       	g.setColor( colour );        	
        g.drawLine(xx, yy, xx + ww, yy);
        g.drawLine(xx, yy, xx, yy + hh);

        g.setColor( Constants.COLOUR_SHADOW );
        g.drawLine(xx + 1, yy + hh, xx + ww, yy + hh);
        g.drawLine(xx + ww, yy, xx + ww, yy + hh);
    }
    
    //</editor-fold>
}
